package be.kuleuven.cs.mop.domain.model.impl;


import java.util.Calendar;
import java.util.GregorianCalendar;

import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.Reservation;



/**
 * Internal implementation of the {@link Reservation} interface
 */
public class ReservationImpl implements Comparable<ReservationImpl>, Reservation {
	
	private final Calendar	date;
	private final long		duration;
	private final Calendar	end			= new GregorianCalendar();
	private final TaskImpl	task;
	private final ResourceImpl	resource;
	
	/**
	 * Creates a new <code>ReservationImpl</code>.
	 * @param task
	 * @param date
	 * @param duration
	 * @throws TaskManagerException
	 */
	protected ReservationImpl(final TaskImpl task, final ResourceImpl resource,
			final Calendar date, final long duration) throws TaskManagerException{
		if(!isValidTask(task)){
			throw new TaskManagerException(TaskManagerException.INVALID_TASK);
		}
		if(!isValidResource(resource)){
			throw new TaskManagerException(TaskManagerException.INVALID_RESOURCE);
		}
		if(!isValidDate(date, task.getUser().getTime())){
			throw new TaskManagerException(TaskManagerException.INVALID_DATE);
		}
		if(!isValidDuration(duration)){
			throw new TaskManagerException(TaskManagerException.INVALID_DURATION);
		}
		this.date		= date;
		this.duration	= duration;
		end.setTimeInMillis(date.getTimeInMillis() + duration);
		this.task		= task;
		this.resource	= resource;
		
	}
	
	
	
	public boolean isValidResource(ResourceImpl resource) {
		return resource != null;
	}



	@Override
	public int compareTo(final ReservationImpl reservation) {
		return getDate().compareTo(reservation.getDate());
	}
	
	/**
	 * Determines whether or not the specified date
	 * 	is between the {@link Reservation}'s start date and end date
	 * @param date - a <code>Calendar</code>
	 */
	public boolean covers(final Calendar date) throws NullPointerException {
		if (date == null)
			throw new NullPointerException("date == NULL");
		return (date.after(this.date) && date.before(end));
	}
	
	@Override
	public boolean equals(final Object obj) {
		if (obj == this)
			return true;
		return ((obj instanceof ReservationImpl)
				&& getDate().equals(((ReservationImpl)obj).getDate()));
	}
	
	@Override
	public Calendar getDate() { return date; }
	
	@Override
	public long getDuration() { return duration; }
	
	@Override
	public Calendar getEnd() { return end; }
	
	/**
	 * Returns the {@link TaskImpl} of this {@link Reservation}
	 */
	public TaskImpl getTask() { return task; }
	
	/**
	 * Returns the {@link ResourceImpl} of this {@link Reservation}
	 */
	public ResourceImpl getResource() { return resource; }
	
	/**
	 * Determines whether or not overlap exists
	 * 	between this and the specified {@link Reservation}'s dates
	 * @param reservation - a <code>ReservationImpl</code>
	 */
	public boolean hasConflict(final ReservationImpl reservation) {
		final Calendar a = getDate();
		final Calendar b = getEnd();
		final Calendar c = reservation.getDate();
		final Calendar d = reservation.getEnd();
		return ((covers(c) || covers(d) || reservation.covers(a) || reservation.covers(b)));
	}
	
	@Override
	public String toString() {
		return new StringBuilder("Reservation:  ")
		.append(date).append(" (").append(task).append(')')
		.toString();
	}
	
	/**
	 * Validates a Task.
	 * @param task
	 */
	public static boolean isValidTask(TaskImpl task){
		return (task != null);
	}
	
	/**
	 * Validates a Date.
	 * @param user
	 */
	public static boolean isValidDate(Calendar date, Clock clock){
		return ((date != null)
				&& (date.getTimeInMillis() >=  clock.getTime().getTimeInMillis()));
	}
	
	/**
	 * Validates a duration.
	 * @param duration
	 */
	public static boolean isValidDuration(long duration){
		return (duration >= 60000);
	}
	
	
	
}